#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/icmp6.h>
#include <netinet/ip6.h>
#include <arpa/inet.h>

#define MAX_PACKET_SIZE 65536
#define BYTES_PER_LINE 16
void hex_dump(const uint8_t *data, size_t length) {
    const uint8_t *ptr = data;
    size_t i, j;

    for (i = 0; i < length; i += BYTES_PER_LINE) {
        printf("%08zx: ", i);  // 输出当前偏移地址

        // 输出当前行的十六进制数据
        for (j = 0; j < BYTES_PER_LINE && (i + j) < length; j++) {
            printf("%02hhx ", ptr[j]);
        }

        // 填充空格，使每行十六进制数据对齐
        for (; j < BYTES_PER_LINE; j++) {
            printf("   ");
        }

        printf("\n");  // 换行
        ptr += BYTES_PER_LINE;
    }
}
// 示例函数：将二进制数据以hexdump格式打印
void hex_dump2(const unsigned char *data, size_t len) {
    const unsigned char *ptr = data;
    size_t row_count = 0;
    size_t i = 0;
    for (i = 0; i < len; ++i) {
        if ((i % 16) == 0) {
            if (i != 0) {
                printf("\n");
            }
            printf("%08x: ", row_count * 16);
            row_count++;
        }
        printf("%02x ", ptr[i]);

        // 输出ASCII表示（如果字符是可见的）
        if ((ptr[i] >= 0x20 && ptr[i] <= 0x7E) || (ptr[i] >= 0xA0 && ptr[i] <= 0xFF)) {
            printf("%c", ptr[i]);
        } else {
            printf(".");
        }
        if ((i + 1) % 16 == 0) {
            printf(" ");
        }
    }

    // 如果数据长度不是16的倍数，补足空白
    while ((i % 16) != 0) {
        printf("   ");
        if ((i + 1) % 16 == 0) {
            printf(" ");
        }
        i++;
    }

    // 输出最后一行的结尾
    printf("\n");

    // 如果数据长度不是16的倍数，填充剩余的ASCII列
    while ((i % 16) != 0) {
        printf(" ");
        i++;
    }
    printf("\n");
}

uint16_t in6_cksum(const struct icmp6_hdr *icmp6, const struct ip6_hdr *ip6, const uint8_t *data, size_t len) {
    uint32_t sum = 0;
    const uint16_t *ptr = (const uint16_t *)icmp6;
    int left = sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr) + len;

    // Add ICMPv6 header
    while (sizeof(struct icmp6_hdr) > 1 && left > 1) {
        sum += *ptr++;
        left -= 2;
    }

    // Add IP header
    ptr = (const uint16_t *)ip6;
    for (int i = 0; i < sizeof(struct ip6_hdr) / 2; ++i) {
        sum += *ptr++;
    }

    // Add data
    ptr = (const uint16_t *)data;
    for (int i = 0; i < len / 2; ++i) {
        sum += *ptr++;
    }

    // Add carry
    while (sum >> 16) {
        sum = (sum & 0xffff) + (sum >> 16);
    }

    return ~sum;
}

int main(int argc, char * argv[]) {
    int sockfd;
    struct sockaddr_in6 addr;
    char buffer[MAX_PACKET_SIZE];
    ssize_t bytes_received;
    //struct ip6_hdr *ip;
    struct icmp6_hdr *icmp;
    u_int payload_len;

    int mtu = 1280;

    if(argc > 1)
    {
	mtu = atoi(argv[0]);
        if(mtu <=0) {
		mtu = 1280;
	}
    }

    // Create a raw socket to listen for ICMPv6 packets
    if ((sockfd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    // 绑定到所有网络接口
    // struct sockaddr_in6 any_addr = {.sin6_family = AF_INET6, .sin6_port = 0};
    // if (bind(sockfd, (struct sockaddr*)&any_addr, sizeof(any_addr)) < 0) {
    //     perror("Failed to bind to all interfaces");
    //     return EXIT_FAILURE;
    // }

    // Listen for ICMPv6 packets
    while (1) {
        memset(&addr, 0, sizeof(addr));
        memset(buffer, 0, sizeof(buffer));
        socklen_t addr_len = sizeof(addr);

        // Receive ICMPv6 packet
        bytes_received = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&addr, &addr_len);
        if (bytes_received == -1 || bytes_received < 80) {
            perror("recvfrom");
            close(sockfd);
            exit(EXIT_FAILURE);
        }
        // Extract IP and ICMP headers
	icmp = (struct icmp6_hdr *)buffer;
	char fullip[MAX_PACKET_SIZE];
	memcpy(fullip + sizeof(struct ip6_hdr), buffer, bytes_received);
        // ip = (struct ip6_hdr *)buffer;
        // icmp = (struct icmp6_hdr *)(buffer + sizeof(struct ip6_hdr));

        // Determine if it's an ICMPv6 echo request and if the payload is too big
        if (icmp->icmp6_type == ICMP6_ECHO_REQUEST) {
	    //hex_dump(buffer, bytes_received>128?128:bytes_received);
            //payload_len = ntohs(ip->ip6_plen);
	    payload_len = bytes_received + 40;
	    // 40 = ip header;
            if (payload_len > mtu) { // Example threshold for maximum payload size
    		char xx[MAX_PACKET_SIZE];
                // Prepare the ICMPv6 error message
                memset(xx, 0, sizeof(xx));
                struct icmp6_hdr *icmp_reply = (struct icmp6_hdr *)xx;
                icmp_reply->icmp6_type = ICMP6_PACKET_TOO_BIG;
                icmp_reply->icmp6_code = 0;
                icmp_reply->icmp6_cksum = 0;
                icmp_reply->icmp6_mtu = htonl(mtu); // Example MTU value
                memcpy(xx + sizeof(struct icmp6_hdr), fullip, sizeof(struct ip6_hdr) + 8); // Copy 8 bytes of echo request data

                // Calculate ICMPv6 checksum
                icmp_reply->icmp6_cksum = in6_cksum(icmp_reply, fullip, (uint8_t *)(fullip + sizeof(struct ip6_hdr)), 8);
		hex_dump(xx, sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr) + 8);
                // Send the ICMPv6 error message
                if (sendto(sockfd, xx, sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr) + 8, 0, (struct sockaddr *)&addr, addr_len) == -1) {
                    perror("sendto");
                }
            }
        }
    }

    close(sockfd);
    return 0;
}

